using System;
using System.Text;

namespace UMC.Net;


public class ByteChunk : IDisposable
{

    public Span<byte> Slice(int start, int length)
    {
        return _Chunk.AsSpan(start, length);
    }
    public void Reset()
    {
        _size = 0;
        _tempSize = 0;
    }
    public byte[] Chunk => _Chunk;
    private byte[] _Chunk;
    int _maxSize = 0;
    int _size, _tempSize;
    public ByteChunk()
    {
        this._Chunk = System.Buffers.ArrayPool<byte>.Shared.Rent(0x200);
        this._maxSize = 0;
    }
    public int Size
    {
        get { return _size; }
    }
    public ByteChunk(int maxSize)
    {
        this._Chunk = System.Buffers.ArrayPool<byte>.Shared.Rent(0x200);
        this._maxSize = maxSize;
    }
    public int MaxSize => _maxSize;
    public void Close()
    {
        if (this._maxSize == 0)
        {
            this.Dispose();
        }
    }
    bool disposing = false;
    public void Dispose()
    {
        if (disposing == false)
        {
            System.Buffers.ArrayPool<byte>.Shared.Return(this._Chunk);
            disposing = true;
        }
    }
    void Expand(int size)
    {

        var len = _size + _tempSize + size;
        if (len >= _Chunk.Length)
        {
            if (_maxSize > 0 && len > _maxSize)
            {
                throw new Exception("Exceeding the maximum capacity");
            }
            else
            {
                var bc = System.Buffers.ArrayPool<byte>.Shared.Rent(len + 0x200);
                Array.Copy(_Chunk, 0, bc, 0, _size + _tempSize);
                System.Buffers.ArrayPool<byte>.Shared.Return(_Chunk);
                this._Chunk = bc;

            }
        }
    }
    public StringValue Rent(int size)
    {
        Expand(size);
        if (_tempSize > 0)
        {
            var tmep = _Chunk.AsSpan(_size, _tempSize);
            tmep.CopyTo(_Chunk.AsSpan(_size + size));
        }
        _size += size;
        return new StringValue(new ByteSegment(_size - size, size, this));
    }
    public StringValue Segment(int index, int size)
    {
        if (index + size <= size + _tempSize)
        {
            return new StringValue(new ByteSegment(index, size, this));
        }
        return StringValue.Empty;
    }


    public StringValue Rent(ReadOnlySpan<byte> bytes)
    {
        Expand(bytes.Length);
        if (_tempSize > 0)
        {
            var tmep = _Chunk.AsSpan(_size, _tempSize);
            tmep.CopyTo(_Chunk.AsSpan(_size + bytes.Length));
        }
        bytes.CopyTo(_Chunk.AsSpan(_size));
        _size += bytes.Length;

        return new StringValue(new ByteSegment(_size - bytes.Length, bytes.Length, this));

    }
    public int Temp(int temp)
    {
        Expand(4);
        BitConverter.TryWriteBytes(_Chunk.AsSpan(_size + _tempSize), temp);
        _tempSize += 4;
        return _tempSize;
    }
    public int Temp(uint temp)
    {
        Expand(4);
        BitConverter.TryWriteBytes(_Chunk.AsSpan(_size + _tempSize), temp);
        _tempSize += 4;
        return _tempSize;
    }

    public int Temp(ulong temp)
    {
        Expand(8);
        BitConverter.TryWriteBytes(_Chunk.AsSpan(_size + _tempSize), temp);
        _tempSize += 8;
        return _tempSize;
    }
    public int Temp(long temp)
    {
        Expand(8);
        BitConverter.TryWriteBytes(_Chunk.AsSpan(_size + _tempSize), temp);
        _tempSize += 8;
        return _tempSize;
    }
    public int Temp(ushort temp)
    {
        Expand(2);
        BitConverter.TryWriteBytes(_Chunk.AsSpan(_size + _tempSize), temp);
        _tempSize += 2;
        return _tempSize;
    }
    public int Temp(byte temp)
    {
        Expand(1);
        _Chunk[_size + _tempSize] = temp;
        _tempSize++;
        return _tempSize;
    }


    public int TempSize => _tempSize;
    public int Temp(ReadOnlySpan<byte> bytes)
    {
        if (bytes.Length > 0)
        {
            Expand(bytes.Length);

            bytes.CopyTo(_Chunk.AsSpan(_size + _tempSize));
            _tempSize += bytes.Length;
        }
        return _tempSize;

    }
    public int Temp(String value)
    {
        var len = UTF8Encoding.UTF8.GetByteCount(value);
        Expand(len);
        _tempSize += UTF8Encoding.UTF8.GetBytes(value, _Chunk.AsSpan(_size + _tempSize, len));
        return _tempSize;

    }
    public void Scale(int scale)
    {
        if (_tempSize > 0)
        {
            if (scale <= _tempSize)
            {
                _tempSize = _tempSize - scale;
                return;
            }
            else
            {
                scale = scale - _tempSize;
                _tempSize = 0;
            }
        }
        if (scale > 0)
        {
            if (scale <= _size)
            {
                _size = _size - scale;
                return;
            }
            else
            {
                _size = 0;
            }

        }

    }
    public StringValue Temp()
    {
        if (_tempSize > 0)
        {
            var v = new StringValue(new ByteSegment(_size, _tempSize, this));
            _size += _tempSize;
            _tempSize = 0;
            return v;
        }
        return StringValue.Empty;
    }
    public StringValue Rent(String value)
    {
        if (String.IsNullOrEmpty(value))
        {
            return StringValue.Empty;
        }
        var len = UTF8Encoding.UTF8.GetByteCount(value);

        Expand(len);

        if (_tempSize > 0)
        {
            var tmep = _Chunk.AsSpan(_size, _tempSize);
            tmep.CopyTo(_Chunk.AsSpan(_size + len));
        }

        _ = UTF8Encoding.UTF8.GetBytes(value, _Chunk.AsSpan(_size, len));
        _size += len;

        return new StringValue(new ByteSegment(_size - len, len, this));


    }
    class ByteSegment : StringSegment
    {
        public ByteSegment(int start, int length, ByteChunk memory)
        {
            this._length = length;
            this._start = start;
            this.mimeMemory = memory;
        }
        ByteChunk mimeMemory;
        int _start, _length;

        public override Memory<byte> Memory => mimeMemory._Chunk.AsMemory(_start, _length);
        public override int Length => _length;
        public override ReadOnlySpan<byte> Span => mimeMemory.Slice(_start, _length);
        public override Span<byte> Value => mimeMemory.Slice(_start, _length);
        public override StringSegment Slice(int start, int length)
        {
            if (start >= 0 && start + length <= this._length)
            {
                return new ByteSegment(_start + start, length, mimeMemory);
            }
            return Empty;
        }
        public override void Output(NetWriteData writeData)
        {
            writeData?.Invoke(mimeMemory._Chunk, _start, _length);

        }
    }
}
